package cn.rocksolid.sport.common.http;

import cn.rocksolid.sport.common.C;
import cn.rocksolid.sport.common.Constant.ECR;
import cn.rocksolid.sport.common.error.RSE;
import cn.rocksolid.sport.common.error.RockSolidException;
import com.google.common.collect.ImmutableMap;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletResponse;
import java.text.DecimalFormat;
import java.util.Map;

/**
 * Spring HTTP framework exception handle adapter
 *
 * @author Axl Zhao
 * @email axl.zhao@163.com
 */
@RestControllerAdvice
public class RockSolidExceptionHandler {

  private static final Logger LOGGER = LoggerFactory.getLogger(RockSolidExceptionHandler.class);

  private static final ThreadLocal<DecimalFormat> CODE_FMT =
          ThreadLocal.withInitial(() -> new DecimalFormat(ECR.ERR_CODE_PATTERN));

  static Map<String, String> error(final int code, final String message) {
    String fmtCode = CODE_FMT.get().format(code);
    CODE_FMT.remove();
    return ImmutableMap.of(C.CODE.p, fmtCode, C.MASSAGE.p, message);
  }

  /**
   * Handle RockSolid exception
   */
  @ExceptionHandler(RockSolidException.class)
  public Map<String, String> handleRockSolidException(final HttpServletResponse response,
          final RockSolidException exp) {
    LOGGER.debug(exp.getMessage(), exp);
    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    return error(exp.getRse().code(), exp.getRse().message());
  }

  /**
   * Handle Authentication exception
   */
  @ExceptionHandler(AuthenticationException.class)
  public Map<String, String> handleAuthenticationException(final HttpServletResponse response,
          final AuthenticationException ace) {
    LOGGER.debug(ace.getMessage(), ace);
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    return error(RSE.UNAUTHORIZED.code(), RSE.UNAUTHORIZED.message());
  }

  /**
   * Handle Authorization exception
   */
  @ExceptionHandler(AuthorizationException.class)
  public Map<String, String> handleAuthorizationException(final HttpServletResponse response,
          final AuthorizationException aze) {
    LOGGER.debug(aze.getMessage(), aze);
    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
    return error(RSE.FORBIDDEN.code(), RSE.FORBIDDEN.message());
  }

  /**
   * Handle others exceptions
   */
  @ExceptionHandler(Exception.class)
  public Map<String, String> handleException(final HttpServletResponse response, final Exception exp) {
    LOGGER.debug(exp.getMessage(), exp);
    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    return error(RSE.UNKNOWN.code(), RSE.UNKNOWN.message());
  }
}
